package city.spring.modules.system.model;

import city.spring.modules.system.entity.ClientEntity;
import city.spring.utils.RoleUtils;
import com.alibaba.fastjson.JSONObject;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.util.StringUtils;

import java.io.Serializable;
import java.util.*;


/**
 * 客户端信息。提供给 Spring Security 程序使用的客户端信息对象
 *
 * @author HouKunLin
 * @date 2019/12/1 0001 22:40
 */
@Data
@EqualsAndHashCode
public class ClientDetailsDTO implements ClientDetails, Serializable {
    /**
     * 客户端ID
     */
    private String clientId;
    /**
     * 客户端密钥
     */
    private String clientSecret;
    /**
     * 资源ID列表
     */
    private Set<String> resourceIds;
    /**
     * 范围列表
     */
    private Set<String> scope;
    /**
     * 授权的授权类型
     */
    private Set<String> authorizedGrantTypes;
    /**
     * 重定向URI
     */
    private Set<String> registeredRedirectUri;
    /**
     * 自动批准范围
     */
    private Set<String> autoApproveScopes;
    /**
     * 权限列表
     */
    private Collection<GrantedAuthority> authorities;
    /**
     * 访问令牌有效性-秒
     */
    private Integer accessTokenValiditySeconds;
    /**
     * 刷新令牌有效性-秒
     */
    private Integer refreshTokenValiditySeconds;
    /**
     * 额外的信息
     */
    private Map<String, Object> additionalInformation;

    public ClientDetailsDTO() {
        setClientId("");
        setClientSecret("");
        setResourceIds("");
        setScope("");
        setAuthorizedGrantTypes("");
        setRegisteredRedirectUri("");
        setAutoApproveScopes("");
        setAuthorities("");
        setAccessTokenValiditySeconds(0);
        setRefreshTokenValiditySeconds(0);
        setAdditionalInformation("");
    }

    public ClientDetailsDTO(ClientEntity clientEntity) {
        setClientId(clientEntity.getClientId());
        setClientSecret(clientEntity.getClientSecret());
        setResourceIds(clientEntity.getResourceIds());
        setScope(clientEntity.getScope());
        setAuthorizedGrantTypes(clientEntity.getAuthorizedGrantTypes());
        setRegisteredRedirectUri(clientEntity.getRegisteredRedirectUri());
        setAutoApproveScopes(clientEntity.getAutoApproveScopes());
        setAuthorities(clientEntity.getAuthorities());
        setAccessTokenValiditySeconds(clientEntity.getAccessTokenValiditySeconds());
        setRefreshTokenValiditySeconds(clientEntity.getRefreshTokenValiditySeconds());
        setAdditionalInformation(clientEntity.getAdditionalInformation());
    }

    @Override
    public String getClientId() {
        return clientId;
    }

    @Override
    public Set<String> getResourceIds() {
        return resourceIds;
    }

    public void setResourceIds(Set<String> resourceIds) {
        this.resourceIds = resourceIds == null ? Collections.emptySet() : new LinkedHashSet<>(resourceIds);
    }

    public void setResourceIds(String resourceIds) {
        this.resourceIds = StringUtils.commaDelimitedListToSet(resourceIds);
    }

    @Override
    public boolean isSecretRequired() {
        return this.clientSecret != null;
    }

    @Override
    public String getClientSecret() {
        return clientSecret;
    }

    @Override
    public boolean isScoped() {
        return this.scope != null && !this.scope.isEmpty();
    }

    @Override
    public Set<String> getScope() {
        return scope;
    }

    public void setScope(Set<String> scope) {
        this.scope = scope == null ? Collections.emptySet() : new LinkedHashSet<>(scope);
    }

    public void setScope(String scope) {
        this.scope = StringUtils.commaDelimitedListToSet(scope);
    }

    @Override
    public Set<String> getAuthorizedGrantTypes() {
        return authorizedGrantTypes;
    }

    public void setAuthorizedGrantTypes(Set<String> authorizedGrantTypes) {
        this.authorizedGrantTypes = authorizedGrantTypes == null ? Collections.emptySet() : new LinkedHashSet<>(authorizedGrantTypes);
    }

    public void setAuthorizedGrantTypes(String authorizedGrantTypes) {
        this.authorizedGrantTypes = StringUtils.commaDelimitedListToSet(authorizedGrantTypes);
    }

    @Override
    public Set<String> getRegisteredRedirectUri() {
        return registeredRedirectUri;
    }

    public void setRegisteredRedirectUri(Set<String> registeredRedirectUri) {
        this.registeredRedirectUri = registeredRedirectUri == null ? Collections.emptySet() : new LinkedHashSet<>(registeredRedirectUri);
    }

    public void setRegisteredRedirectUri(String registeredRedirectUri) {
        this.registeredRedirectUri = StringUtils.commaDelimitedListToSet(registeredRedirectUri);
    }

    @Override
    public Collection<GrantedAuthority> getAuthorities() {
        return authorities;
    }

    public void setAuthorities(Collection<GrantedAuthority> authorities) {
        this.authorities = new ArrayList<>(authorities);
    }

    public void setAuthorities(Set<String> values) {
        // 默认角色，每个用户都拥有的角色
        String defaultRole = RoleUtils.getAuthorityWithDefaultPrefix("CLIENT_USER");
        if (!values.contains(defaultRole)) {
            values = new LinkedHashSet<>(values);
            values.add(defaultRole);
        }
        setAuthorities(AuthorityUtils.createAuthorityList(values.toArray(new String[0])));
    }

    public void setAuthorities(String authorities) {
        setAuthorities(StringUtils.commaDelimitedListToSet(authorities));
    }

    @Override
    public Integer getAccessTokenValiditySeconds() {
        return accessTokenValiditySeconds;
    }

    @Override
    public Integer getRefreshTokenValiditySeconds() {
        return refreshTokenValiditySeconds;
    }

    @Override
    public boolean isAutoApprove(String scope) {
        if (autoApproveScopes == null) {
            return false;
        }
        for (String auto : autoApproveScopes) {
            if ("true".equals(auto) || scope.matches(auto)) {
                return true;
            }
        }
        return false;
    }

    @Override
    public Map<String, Object> getAdditionalInformation() {
        return Collections.unmodifiableMap(this.additionalInformation);
    }

    public void setAdditionalInformation(Map<String, Object> additionalInformation) {
        this.additionalInformation = additionalInformation == null ? Collections.emptyMap() : new LinkedHashMap<>(additionalInformation);
    }

    public void setAdditionalInformation(String json) {
        JSONObject jsonObject = JSONObject.parseObject(json);
        this.additionalInformation = jsonObject == null ? Collections.emptyMap() : new LinkedHashMap<>();
    }

    public void addAdditionalInformation(String key, Object value) {
        this.additionalInformation.put(key, value);
    }

    public void setAutoApproveScopes(Set<String> autoApproveScopes) {
        this.autoApproveScopes = autoApproveScopes == null ? Collections.emptySet() : new LinkedHashSet<>(autoApproveScopes);
    }

    public void setAutoApproveScopes(String autoApproveScopes) {
        this.autoApproveScopes = StringUtils.commaDelimitedListToSet(autoApproveScopes);
    }
}
